//============================================================================
// Name        : Lynx32.cpp
// Author      : White Rabbit
// Version     : 0x01v
// Copyright   : @Copyleft 
// Description : Lynx Virtual Machine based on RISC
//============================================================================
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


typedef unsigned int	l_uint_t;
typedef unsigned char	l_uchar_t;
typedef bool			l_flag_t;
typedef const char*		l_charptr_t;
#define DEBUG
#define RAM_SIZE 		0x100
#define BYTECODE_SIZE   65535
#define REGISTER_NUM	31
class VM {
public:
	l_uint_t registers[REGISTER_NUM];
	l_uint_t ram[RAM_SIZE];
	l_uchar_t bytecode[BYTECODE_SIZE];

	//FLAGS
	l_flag_t flags[4];
	/*
	 * [0] EQ
	 * [3] ERR
	 *
	 */
	enum EFLAGS {
		EQ = 0,
		ERR = 3
	};
	unsigned int IP;
	bool exit_now;

	bool LoadBytecode(l_charptr_t fname) {
		memset(bytecode, 0, sizeof(bytecode)); // halt
		FILE *f = fopen(fname, "rb");
		if(!f) { perror("lynx:fopen:err"); return false; }
		fread(bytecode, 1, 0x1000, f);
		fclose(f);
		return true;
	}
	void WriteReport(void)
    {
        FILE *report;
        report = fopen("report!.txt", "w");
        for(int j=0;j<RAM_SIZE;j++)
			fprintf(report,"$%d=hex(%X)dec(%d)\r\n", j, ram[j], ram[j]);
        fprintf(report,"\r\nCOMPILE TIME %s %s \r\n", __DATE__, __TIME__);
        fclose(report);
    }

    void PrintStatus(void)
    {
        puts("MACHINE STATUS:");
        puts("REGISTERS: ");
        for(int i=0;i<=REGISTER_NUM;i++)
			printf("$%d = %d \n", i,registers[i]);
        printf("IP=%d \n", IP);
        printf("EQ flag=%d\n" \
        	   "ERR flag = %d\n",
        	   flags[0], flags[3]); //TODO: print flags here
        WriteReport();
	}

	void Run(void) {
		memset(ram, 0xFF, sizeof(ram));
		memset(registers, 0xFF, sizeof(registers));
		IP = 0;
		exit_now = false;
		while(!exit_now && IP < BYTECODE_SIZE) {
			IP ++;
			switch(bytecode[IP - 1]) {
			case 0xFF:					//XXX HALT
#ifdef DEBUG
				puts("Machine stopped: HALT OPCODE");
				PrintStatus();
#endif
				exit_now = true;
				break;
			case 0x01:					//XXX LDR <reg>, <imm32> 		//Load Data to register
				registers[bytecode[IP]] = *(l_uint_t*)&bytecode[IP+1];

#ifdef DEBUG
				puts("LDR");
#endif
				IP++;
				break;
			case 0x02:					//XXX STORE <reg>, <addr reg32> 	//Store the register to ram
				ram[bytecode[IP]] = registers[bytecode[IP+1]];
#ifdef DEBUG
				puts("STORE");
#endif
				IP += 2;
				break;
			case 0x03: 					//XXX LOAD <reg>, <addr reg32>	//Load the ram element to register
				registers[bytecode[IP]] = ram[registers[IP+1]];
#ifdef DEBUG
				puts("LOAD");
#endif
				IP += 2;
				break;
			case 0x04: 					//XXX ADD <reg>, <reg>, <reg>
				registers[bytecode[IP]] = registers[bytecode[IP+1]] + registers[bytecode[IP+2]];
#ifdef DEBUG
				puts("ADD");
#endif
				IP+=3;
				break;
			case 0x05:					//XXX SUB <reg>, <reg>, <reg>
				registers[bytecode[IP]] = registers[bytecode[IP+1]] - registers[bytecode[IP+2]];
#ifdef DEBUG
				puts("SUB");
#endif
				IP+=3;
				break;
			case 0x06:					//XXX MUL <reg>, <reg>, <reg>
				registers[bytecode[IP]] = registers[bytecode[IP+1]] * registers[bytecode[IP+2]];
#ifdef DEBUG
				puts("MUL");
#endif
				IP+=3;
				break;
			case 0x07:					//XXX DIV <reg>, <reg>, <reg>
				if(registers[bytecode[IP+2]] == 0) {
					puts("ERROR");
					exit_now = true;
					flags[3] = true;

				}else{
					registers[bytecode[IP]] = registers[bytecode[IP+1]] / registers[bytecode[IP+2]];
					IP+=3;
				}
				break;
			case 0x08:					//XXX CMP <reg>, <reg>
				if( registers[bytecode[IP]] == registers[bytecode[IP+1]]) {
					flags[0] = true;
#ifdef DEBUG
					puts("TRUE");
#endif
				}else {
					flags[0] = false;
#ifdef DEBUG
					puts("FALSE");
#endif
				}
				IP+=2;
				break;
			case 0x09:					//XXX AND <reg>, <reg>, <reg>
				registers[bytecode[IP]] = registers[bytecode[IP+1]] & registers[bytecode[IP+2]];
#ifdef DEBUG
				puts("AND");
#endif
				IP+=3;
				break;
			case 0x0A:					//XXX OR <reg>, <reg>, <reg>
				registers[bytecode[IP]] = registers[bytecode[IP+1]] | registers[bytecode[IP+2]];
#ifdef DEBUG
				puts("OR");
#endif
				IP+=3;
				break;
			case 0x0B:					//XXX XOR <reg>, <reg>, <reg>
				registers[bytecode[IP]] = registers[bytecode[IP+1]] ^ registers[bytecode[IP+2]];
#ifdef DEBUG
				puts("XOR");
#endif
				IP+=3;
				break;
			case 0x0C:					//XXX NOT <reg>
				registers[bytecode[IP]] = ~ registers[bytecode[IP]];
				IP++;
				break;
			case 0x0D:					//XXX SET <flag>, <state>

				break;
			case 0xEE:					//XXX DBGPRINT
				PrintStatus();
				IP++;
				break;
			}
		}
		free_mem();
	}
private:
	void free_mem() { //xxx free mem not secure!
		//maybe...
	}
};

int main(int argc, char *argv[]) {
	if(argc < 2) {
		puts("Lynx 0x01v by White Rabbit");
#ifdef DEBUG
		puts("DEBUG version");
#endif
		puts("use: lynx <bc>");
		puts("where: <bc> - Lynx bytecode");
		puts("To compile use lynxc <file> -o <outname>");
		getchar();
		return 1;
	}
	/*
	VM vm;
	vm.LoadBytecode(argv[1]);
	vm.Run(); */

	VM* vm = new VM;
	vm->LoadBytecode(argv[1]);
	vm->Run();
	delete vm;
	return 0;
}

/* switch(bytecode[ip - 1]) {
	case 0xff: exit_now = true; break;
	case 0x00: r[bytecode[ip]] += r[bytecode[ip+1]]; ip += 2; break;
	case 0x01: r[bytecode[ip]] -= r[bytecode[ip+1]]; ip += 2; break;
	case 0x10: scanf("%i", &r[bytecode[ip]]); ip++; break;
	case 0x20: printf("%.8x\n", r[bytecode[ip]]); ip++; break;
	case 0x30: ram[r[0]] = r[bytecode[ip]]; ip++; break;
	case 0x31: r[bytecode[ip]] = ram[r[0]]; ip++; break;
	case 0x32: r[bytecode[ip]] = *(unsigned int*)&bytecode[ip+1];
break;

}*/

